#pragma once

#include <boost/random.hpp>
#include <math/vector2.hpp>
#include <math/vector3.hpp>
#include <math/vector4.hpp>
#include <math/matrix2x2.hpp>
#include <math/matrix3x3.hpp>
#include <math/matrix4x4.hpp>
#include <math/quaternion.hpp>
#include <math/types.hpp>

#include <d3dx9math.h>




class RandomCache
{
public:

	typedef std::vector<float> Cache;

private:

	boost::mt19937               m_rng;
	boost::uniform_real<float>   m_distribution;
	boost::variate_generator< boost::mt19937&, boost::uniform_real<float> > m_die;

	Cache   m_cache;

public:

	/**
	 * Create a random-number cache.
	 * The resulting numbers will be in the specified range, or
	 * [-10, 10] when nothing is specified.
	 */
	RandomCache(int min = -10, int max = 10);



	/**
	 * Get a list of random numbers.
	 */
	Cache values(std::size_t count);

	template <typename T>
	T generate()
	{
		return value<T>(*this);
	}

private:

	/**
	 * Insert the given number of elements into the random-cache.
	 */
	void insert(std::size_t number);
};
///////////////////////////////////////////////////////////////////////////////////////////////////




typedef math::types<float> testing_types;
typedef testing_types::vector2      float2;
typedef testing_types::vector3      float3;
typedef testing_types::vector4      float4;
typedef testing_types::quaternion   quaternion;
typedef testing_types::matrix2x2    float2x2;
typedef testing_types::matrix3x3    float3x3;
typedef testing_types::matrix4x4    float4x4;
typedef testing_types::angle        angle;




template <typename T>
T value(RandomCache& cache);

template <> inline
float value(RandomCache& cache)
{
	return cache.values(1).front();
}
///////////////////////////////////////////////////////////////////////////////////////////////////

template <> inline
float2 value<float2>(RandomCache& cache)
{
	RandomCache::Cache tmp = cache.values(2);
	return float2(tmp[0], tmp[1]);
}
///////////////////////////////////////////////////////////////////////////////////////////////////

template <> inline
D3DXVECTOR2 value<D3DXVECTOR2>(RandomCache& cache)
{
	RandomCache::Cache tmp = cache.values(2);
	return D3DXVECTOR2( tmp[0], tmp[1] );
}
///////////////////////////////////////////////////////////////////////////////////////////////////

template <> inline
float3 value<float3>(RandomCache& cache)
{
	RandomCache::Cache tmp = cache.values(3);
	return float3(tmp[0], tmp[1], tmp[2]);
}
///////////////////////////////////////////////////////////////////////////////////////////////////

template <> inline
D3DXVECTOR3 value<D3DXVECTOR3>(RandomCache& cache)
{
	RandomCache::Cache tmp = cache.values(3);
	return D3DXVECTOR3( tmp[0], tmp[1], tmp[2] );
}
///////////////////////////////////////////////////////////////////////////////////////////////////

template <> inline
float4 value<float4>(RandomCache& cache)
{
	RandomCache::Cache tmp = cache.values(4);
	return float4(tmp[0], tmp[1], tmp[2], tmp[3]);
}
///////////////////////////////////////////////////////////////////////////////////////////////////

template <> inline
D3DXVECTOR4 value<D3DXVECTOR4>(RandomCache& cache)
{
	RandomCache::Cache tmp = cache.values(4);
	return D3DXVECTOR4( tmp[0], tmp[1], tmp[2], tmp[3] );
}
///////////////////////////////////////////////////////////////////////////////////////////////////

template <> inline
quaternion value<quaternion>(RandomCache& cache)
{
	RandomCache::Cache tmp = cache.values(4);
	return quaternion(tmp[0], tmp[1], tmp[2], tmp[3]);
}
///////////////////////////////////////////////////////////////////////////////////////////////////

template <> inline
D3DXQUATERNION value<D3DXQUATERNION>(RandomCache& cache)
{
	RandomCache::Cache tmp = cache.values(4);
	D3DXQUATERNION q;
	q.w = tmp[0];
	q.x = tmp[1];
	q.y = tmp[2];
	q.z = tmp[3];
	return q;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

template <> inline
float2x2 value<float2x2>(RandomCache& cache)
{
	RandomCache::Cache tmp = cache.values(4);
	return float2x2( tmp[0], tmp[1], tmp[2], tmp[3] );
}
///////////////////////////////////////////////////////////////////////////////////////////////////

template <> inline
float3x3 value<float3x3>(RandomCache& cache)
{
	RandomCache::Cache tmp = cache.values(9);
	return float3x3( tmp[0], tmp[1], tmp[2],
		tmp[3], tmp[4], tmp[5],
		tmp[6], tmp[7], tmp[8] );
}
///////////////////////////////////////////////////////////////////////////////////////////////////

template <> inline
float4x4 value<float4x4>(RandomCache& cache)
{
	RandomCache::Cache tmp = cache.values(16);
	return float4x4( tmp[0],  tmp[1],  tmp[2],  tmp[3],
		tmp[4],  tmp[5],  tmp[6],  tmp[7],
		tmp[8],  tmp[9],  tmp[10], tmp[11],
		tmp[12], tmp[13], tmp[14], tmp[15 ] );
}
///////////////////////////////////////////////////////////////////////////////////////////////////

template <> inline
D3DXMATRIX value<D3DXMATRIX>(RandomCache& cache)
{
	RandomCache::Cache tmp = cache.values(16);
	return D3DXMATRIX( tmp[0],  tmp[1],  tmp[2],  tmp[3],
		tmp[4],  tmp[5],  tmp[6],  tmp[7],
		tmp[8],  tmp[9],  tmp[10], tmp[11],
		tmp[12], tmp[13], tmp[14], tmp[15 ] );
}
///////////////////////////////////////////////////////////////////////////////////////////////////
